"""
功能：抓取和保存业绩预告和业绩快报
"""
from AStock.ASEvent import Worker
from AStock.ASSetting import settings, cons
from AStock.ASUtil import date_to_bar_format, date_from_bar_format, ASHttpParser
from AStock.ASData import financial_dict
import json
from pymongo import UpdateOne


class ASFinancialPreviewCrawler(Worker):

    def __init__(self):
        super(ASFinancialPreviewCrawler, self).__init__()
        self._param_dict = {'yjyg_proxy': None, 'yjkb_proxy': None}
        if settings.financial_source_yjyg == cons.CrawlSource.EASTMONEY:
            self._param_dict['yjyg_url'] = cons.EASTMONEY_FINANCIAL_YJYG
            self._param_dict['yjyg_date_mapping'] = self._eastmoney_date
            self._param_dict['yjyg_handler'] = self._eastmoney_text_handler
        else:
            raise NotImplementedError

        if settings.financial_source_yjkb == cons.CrawlSource.EASTMONEY:
            self._param_dict['yjkb_url'] = cons.EASTMONEY_FINANCIAL_YJKB
            self._param_dict['yjkb_date_mapping'] = self._eastmoney_date
            self._param_dict['yjkb_handler'] = self._eastmoney_text_handler
        else:
            raise NotImplementedError

    @staticmethod
    def _eastmoney_date(date):
        return date_to_bar_format(date)

    @staticmethod
    def _eastmoney_text_handler(text, **kwargs):
        """
        解析响应的文本
        :param text: str，待解析的响应文本
        :return: dict，
            失败时，None
            成功时，{'page_no': 1, 'pages': 105, 'data': [{...}, {...}, ...]}
            其中，data为业绩预告/快报数据：
            {
                'code': '300001',
                'notice_date': '20200821',
                'forecast_content': '预计2020年1-3月归属于上市公司股东的净利润盈利:约6,680万元-8,164万元,同比增长约22.45%-49.66%。',
                'change_reason': '2020年1-3月经营业绩有所下降,主要系受疫情影响',
                'forecast_type': '略减',
                'forecast_net_profit': 11545000,  # 净利（扣非）
                'forecast_income': 741553800  # 营收
            }
            分别对应：SECURITY_CODE, NOTICE_DATE, FORECASTCONTENT, CHANGEREASONDSCRPT, FORECASTTYPE,
                        FORECASTJZ/PARENT_NETPROFIT, TOTAL_OPERATE_INCOME
        """
        try:
            event_type = kwargs['event_type']
        except KeyError as e:
            print('error parse eastmoney financial preview: {}'.format(e))
            return None
        if event_type not in ['yjyg', 'yjkb']:
            print('error parse eastmoney financial preview, unknown event type {}'.format(event_type))
            return None

        if event_type == 'yjyg':
            s = 'var cSSQORwc='  # 与URL中的var参数一致
        else:
            s = 'var EOWpZXXh='  # 与URL中的var参数一致

        if not text.startswith(s) or len(text) <= len(s):
            return None
        charset = kwargs.get('charset', 'utf-8')
        try:
            js = json.loads(text[len(s):-1], encoding=charset)
        except json.JSONDecodeError as e:
            print('error parse eastmoney financial preview {}: {}'.format(event_type, e.msg))
            return None

        try:
            res = {'page_no': kwargs.get('page_no'), 'pages': None, 'data': []}
            if js['success']:
                result = js['result']
                res['pages'] = result['pages']
                for item in result['data']:
                    res['data'].append({
                        'code': item['SECURITY_CODE'],
                        'notice_date': int(date_from_bar_format(item['NOTICE_DATE'][:len('2020-03-31')])),
                        'forecast_content': item.get('FORECASTCONTENT', ''),
                        'change_reason': item.get('CHANGEREASONDSCRPT', ''),
                        'forecast_type': item.get('FORECASTTYPE', ''),
                        'forecast_net_profit': item['FORECASTJZ'] if event_type == 'yjyg' else item['PARENT_NETPROFIT'],
                        'forecast_income': item.get('TOTAL_OPERATE_INCOME', None)
                    })
                return res
        except ValueError as e:
            print('error parse eastmoney financial preview {}: {}'.format(event_type, e))
            return None
        return None

    def run(self, event_data):
        """
        执行抓取任务
        :param event_data: dict，包括：
            event_type: 'yjyg' or 'yjkb'  业绩预告 业绩快报
            report_date: str，业绩预告/业绩快报日期（20190331 20190630 20199030 20191231），格式为 yyyymmdd
            page_no: int，页码
            page_size: int，页大小
            rand: int，随机数

        :return: dict，
            失败时，返回的字典为 {'error': 'error string'}
            成功时，{'page_no': 1, 'pages': 105, 'data': [{...}, {...}, ...]}
            其中，data为业绩预告/快报数据：
            {
                'code': '300001',
                'notice_date': '20200821',
                'forecast_content': '预计2020年1-3月归属于上市公司股东的净利润盈利:约6,680万元-8,164万元,同比增长约22.45%-49.66%。',
                'change_reason': '2020年1-3月经营业绩有所下降,主要系受疫情影响',
                'forecast_type': '略减',
                'forecast_net_profit': 11545000,  # 净利（扣非）
                'forecast_income': 741553800  # 营收
            }
            分别对应：SECURITY_CODE, NOTICE_DATE, FORECASTCONTENT, CHANGEREASONDSCRPT, FORECASTTYPE,
                        FORECASTJZ/PARENT_NETPROFIT, TOTAL_OPERATE_INCOME
        """
        try:
            event_type = event_data['event_type']
            report_date = event_data['report_date']
            page_no = event_data['page_no']
            page_size = event_data['page_size']
            rand = event_data['rand']
        except KeyError as e:
            return {'error': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['yjyg', 'yjkb']:
            return {'error': 'event type error: {}'.format(event_type)}

        date_mapping = self._param_dict.get(event_type + '_date_mapping')
        if date_mapping:
            report_date = date_mapping(report_date)

        url = self._param_dict[event_type + '_url'].format(
            pageno=page_no, reportdate=report_date, pagesize=page_size, rand=rand)
        parser = ASHttpParser(
            url,
            cons.HEADER,
            self._param_dict[event_type + '_proxy'],
            content_type="text"
        )
        res = parser.parse_text(
            text_handler=self._param_dict[event_type + '_handler'],
            charset=parser.charset,
            page_no=page_no,
            event_type=event_type
        )
        if res is None:
            return {'error': 'error parse eastmoney financial preview {}, url: {}'.format(event_type, url)}
        return res


class ASFinancialPreviewSaver(Worker):

    def __init__(self):
        super().__init__()
        # 英文名-字段编号，后续如果需要调整字段编号对应的财务项目
        # 只需要将financial_dict中的英文名调整到对应的编号下即可
        self._financial_dict = {
            v: k[:3] for k, v in financial_dict.items()}

    def run(self, event_data):
        """
        执行保存财务报表发布日期的任务
        :param event_data: dict，包括：
            event_type: 'yjyg' or 'yjkb'  业绩预告 业绩快报
            report_date: str，报表日期，格式为 yyyymmdd
            data: list，每个元素为业绩预告/快报数据：
            {
                'code': '300001',
                'notice_date': '20200821',
                'forecast_content': '预计2020年1-3月归属于上市公司股东的净利润盈利:约6,680万元-8,164万元,同比增长约22.45%-49.66%。',
                'change_reason': '2020年1-3月经营业绩有所下降,主要系受疫情影响',
                'forecast_type': '略减',
                'forecast_net_profit': 11545000,  # 净利（扣非）
                'forecast_income': 741553800  # 营收
            }
        :return: dict，
            成功：{'succ': 'saved: 20200630    financial preview'}
            失败：{'fail': 'failed to save: 20200630    financial preview'}
        """
        try:
            event_type = event_data['event_type']
            report_date = event_data['report_date']
            data = event_data['data']
        except KeyError as e:
            return {'fail': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['yjyg', 'yjkb']:
            return {'fail': 'event type error: {}'.format(event_type)}

        succ = 'saved: {}   financial preview {}'.format(report_date, event_type)
        fail = 'failed to save: {}   financial preview {}'.format(report_date, event_type)

        bulk = []
        for item in data:
            if event_type == 'yjyg':
                set_data = {
                    '$set': {
                        'code': item['code'],
                        'report_date': int(report_date),
                        self._financial_dict['financialYjygDate']: item['notice_date'],
                        self._financial_dict['forecastContent']: item['forecast_content'],
                        self._financial_dict['changeReason']: item['change_reason'],
                        self._financial_dict['forecastType']: item['forecast_type'],
                        self._financial_dict['forecastNetProfit']: item['forecast_net_profit']
                    }
                }
            else:
                set_data = {
                    '$set': {
                        'code': item['code'],
                        'report_date': int(report_date),
                        self._financial_dict['financialYjkbDate']: item['notice_date'],
                        self._financial_dict['forecastNetProfit']: item['forecast_net_profit'],
                        self._financial_dict['forecastIncome']: item['forecast_income']
                    }
                }
            update_one = UpdateOne(
                {'code': item['code'], 'report_date': int(report_date)},
                set_data,
                upsert=True
            )
            bulk.append(update_one)

        try:
            coll = settings.database.financial  # 文档集合对象
            coll.bulk_write(bulk)
        except Exception as e:
            return {'fail': fail + '    ' + str(e)}

        return {'succ': succ}

